﻿<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
    <title></title>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Sofire v1.0 开源——快速数据库访问模式 Sofire.Data（2） - Treenew Lyn - 博客园</title>
    <link type="text/css" rel="stylesheet" href="http://common.cnblogs.com/blog/css/common.css?id=20120623" />
    <link type="text/css" rel="stylesheet" href="http://common.cnblogs.com/blog/css/common2.css" />
    <link type="text/css" rel="stylesheet" href="http://common.cnblogs.com/css/shStyle.css" />
    <link type="text/css" rel="stylesheet" href="http://www.cnblogs.com/sofire/customcss.aspx?id=62905" />
    <link title="RSS" type="application/rss+xml" rel="alternate" href="http://www.cnblogs.com/sofire/rss" />
    <link title="RSD" type="application/rsd+xml" rel="EditURI" href="http://www.cnblogs.com/sofire/rsd.xml" />
    <link type="application/wlwmanifest+xml" rel="wlwmanifest" href="http://www.cnblogs.com/sofire/wlwmanifest.xml" />
    <script src="http://common.cnblogs.com/script/jquery.js" type="text/javascript"></script>
    <script type="text/javascript">
        var currentBlogApp = 'sofire';    </script>
    <script src="http://common.cnblogs.com/blog/script/common.js?id=20120623" type="text/javascript"></script>
    <script src="http://common.cnblogs.com/script/json2.js" type="text/javascript"></script>
    <script type="text/javascript" src="http://common.cnblogs.com/script/syntaxHighlighter.js"></script>
    <style type="text/css">
        /*最新评论*/
        #myposts .PostList
        {
            font-size: 14px;
            line-height: 24px;
            margin: 10px 0;
            padding: 20px;
            background: #F2F2F2;
            box-shadow: 0 0 5px #aaa;
        }
        .Abstract
        {
            padding: 15px;
            border: dotted 2px #999;
            color: #999;
            font-family: 'Microsoft Yahei';
            border-radius: 4px;
        }
        
        .First
        {
            margin: 10px 0;
            font-family: 'Microsoft Yahei';
            text-align: left;
            padding: 6px 20px;
            color: #fff;
            background: #4D7CC6;
            font-size: 20px;
            border-radius: 4px;
            clear: both;
        }
        
        
        .Second
        {
            margin: 10px 0;
            font-family: 'Microsoft Yahei';
            padding: 6px 20px;
            background: #4DA6C6;
            color: #fff;
            font-size: 18px;
            border-radius: 4px;
            clear: both;
        }
        
        
        .Third
        {
            margin: 10px 0;
            padding: 6px 20px;
            font-family: 'Microsoft Yahei';
            margin: 15px 0;
            font-size: 16px;
            color: fff;
            background: #86C4D8;
            color: #fff;
            border-radius: 4px;
            clear: both;
        }
        .note
        {
            margin: 10px 0;
            padding: 15px 20px 15px 60px;
            background: #FCFAA9 url('http://images.cnblogs.com/cnblogs_com/libaoheng/305804/o_yellow-pin.png') no-repeat 20px 0;
            font-size: 15px;
            font-family: 'Microsoft Yahei';
            box-shadow: 0 0 8px #aaa;
            clear: both;
        }
        
        .demo
        {
            text-align: left;
            padding: 6px 20px;
            overflow: auto;
            border-radius: 4px;
            background: orange;
            color: #fff;
            font-size: 16px;
            clear: both;
        }
        
        .cnblogs_Highlighter
        {
            border: solid 1px #ccc;
            clear: both;
        }
        
        .cnblogs_code
        {
            background: #EFFFF4;
            border: solid 0px #939393;
            font-size: 14px;
            clear: both;
            padding: 10px 20px;
        }
        .cnblogs_code pre
        {
            font-size: 14px;
        }
        .cnblogs_code span
        {
            font-family: Courier New;
            font-size: 14px;
        }
        #cnblogs_post_body .MultiContent p
        {
            text-indent: 30px;
        }
    </style>
</head>
<body>
    <div class="postBody">
        <div id="cnblogs_post_body">
            <div class="note">
                <p>
                    Sofire Suite 是一套个人从 2009 年 08 月开始着手研发的套件。历经几年的不断优化改进，从最初的 V 套件到 Sofire2011 到目前的 Sofire.v1.0，Sofire 已经经历了许多项目的考验，并且出色的完成它的使命。现在，我将这套组件再次重构，<span style="color: #ff0000;"><strong>尝试让它成为任意平台、框架、套件的的底层首选</strong></span>。秉着开源精神，希望这套组件在博友的讨论中不断成长、成熟。</p>
                <p>
                    本文主要介绍是概念版的—— SOFIRE XML SQL，我们称之为 X-SQL。</p>
                <p>
                    <strong>目录</strong></p>
                <ul>
                    <li><a href="#WhatisXSql">什么是 X-SQL？</a></li>
                    <li><a href="#XSqlSupport">X-SQL 支持什么功能？</a></li>
                    <li><a href="#XSqlNodes">X-SQL 元素</a>
                        <ul>
                            <li><a href="#XSqlNode_document">document 元素</a></li>
                            <li><a href="#XSqlNode_xsql">xsql 元素</a></li>
                            <li><a href="#XSqlNode_expression">expression 元素</a></li>
                            <li><a href="#XSqlNode_if_else">if-else 元素</a></li>
                            <li><a href="#XSqlNode_switch_case_default">switch-case-default 元素</a></li>
                            <li><a href="#XSqlNode_foreach">foreach 元素</a></li>
                            <li><a href="#XSqlNode_include">include 元素</a></li>
                            <li><a href="#XSqlNode_trim">trim 元素</a></li>
                            <li><a href="#XSqlNode_parameters">parameters 元素</a></li>
                        </ul>
                    </li>
                    <li><a href="#XSqlDemos">X-SQL 的实践</a></li>
                    <li><a href="#XSqlPerformance">X-SQL 的性能</a></li>
                    <li><a href="#XSqlEnd">最后</a></li>
                </ul>
            </div>
            <div id="WhatisXSql" class="First">
                什么是 X-SQL？
            </div>
            <div class="MultiContent">
                <p>
                    前两个礼拜，某个同事在公司内部，举办了一场小型培训会。他给大家绍 JAVA 的一个开源套件——MyBatis（<a href="http://baike.baidu.com/view/4372646.htm" title="MyBatis 的百度百科">百度百科</a>） 。</p>
                <p>
                    关于 Mybatis 含有一个非常重要的功能，就是 XML 化 T-SQL 语句（也成为动态 T-SQL）。它彻底的将 T-SQL 语句从代码层上剥离，最大程度的动态化，也提高了产品的可维护性。</p>
                <p>
                    或许，这只是 Mybatis 的功能之一，我并没有对它的其他功能进行深入研究。不过我却对它的设计理念充满兴趣，培训会结束后，我便搜寻有基于 .NET 版的 Mybatis。</p>
                <p>
                    <a href="http://code.google.com/p/mybatisnet/">Mybatis.NET</a>，总体来说还算不错，但它有一些与 JAVA 版的 Mybatis 不同，比如在条件判断上他并不是采用 "test" 属性进行验证，而是通过条件 XML 元素（如 isNotNull XML 元素之类）进行判定。</p>
                <div class="Third">
                    Mybatis for .NET 示例</div>
                <div class="cnblogs_Highlighter">
                    <pre class="brush:html;collapse:true;;gutter:false;">
    &lt;insert id="InsertAccountDynamic" parameterClass="Account"&gt;
      INSERT INTO Accounts
      (Account_ID, Account_FirstName, Account_LastName, Account_Email)
      VALUES(#Id#, #FirstName#, #LastName#
      &lt;dynamic prepend=","&gt;
        &lt;isNotNull prepend="," property="EmailAddress"&gt;
          #EmailAddress#
        &lt;/isNotNull&gt;
        &lt;isNull prepend="," property="EmailAddress"&gt;
          null
        &lt;/isNull&gt;
      &lt;/dynamic&gt;
      )
    &lt;/insert&gt;
                    </pre>
                </div>
                <div class="Third">
                    Mybatis for JAVA 示例
                </div>
                <div class="cnblogs_Highlighter">
                    <pre class="brush:html;collapse:true;;gutter:false;">
    &lt;update id="updateStudent_if_set" parameterType="liming.student.manager.data.model.StudentEntity"&gt;
	    UPDATE STUDENT_TBL
	    &lt;set&gt;
		    &lt;if test="studentName != null and studentName != '' "&gt;
			    STUDENT_TBL.STUDENT_NAME = #{studentName},
		    &lt;/if&gt;
	    &lt;/set&gt;
	    WHERE STUDENT_TBL.STUDENT_ID = #{studentId};	
    &lt;/update&gt;
                    </pre>
                </div>
                <p>
                    从上面的 XML 可以看出，Mybatis.NET 的 XML 表达式判定语法显得十分臃肿，在多条件（如“userid=1 or userid=2”）无法友好的展示。所以……我的臭毛病又爆发了——“造轮子”。
                </p>
                <p>
                    好吧，我承认我的轮子已经造的过多了……</p>
            </div>
            <div id="XSqlSupport" class="First">
                X-SQL 支持什么功能？
            </div>
            <div class="MultiContent">
                <p>
                    从整体功能方面，我尽可能的参考 Mybatis，但并不会失去 X-SQL 本身存在的特点（高效什么等，我就不扯蛋了）：</p>
                <ul>
                    <li>X-SQL 元素包含：xsql、expression（表达式）、if-else、switch-case-default、foreach、include（引用）、trim（修剪）、parameters（手动定义参数）</li>
                    <li>内置缓存机制（None、Names、Values、Forever）。</li>
                    <li>生成 Sofire.Data.ExecuteCommand 对象（不理解此对象的可以参考 <a href="http://www.cnblogs.com/sofire/archive/2012/06/04/2513867.html" target="_blank">此篇文章</a>）。</li>
                    <li>支持参数绑定语法。拒绝拼接语法，参数化 T-SQL。当然，也支持非参数化参数绑定。<strong>这点与 Mybatis 完全不同。</strong></li>
                    <li>支持 test 表达式以及自定义函数（解释器是亮点）：
                        <pre class="brush:html;collapse:true;;gutter:false;">0、关键字（所有关键字、参数、函数均匹配大小写）
	and、or、not、true、false、null、if、E（检查一下）
1、基本数据类型（Integer、String、DateTime、Float、Boolean）
2、支持
					x：数字		s：字符串		d：日期		o：任意类型
	数学函数	：Abs(x)、Acos(x)、Asin(x)、Atan(x)、Ceiling(x)、Cos(x)、Exp(x)、Floor(x)
				：IEEERemainder(x1,x2)、Log(x1,x2)、Log10(x)、Pow(x1,x2)、Round(x1,x2)、Sign(x)
				：Sin(x)、Sqrt(x)、Tan(x)、Truncate(x)、Max(x1,x2)、Min(x1,x2)、Floor(x)

	字符串函数	：isEmpty(s)、len(s)、lower(s)、upper(s)
				：trim(s)、ltrim(s)、rtrim(s)
				：contains(s1,s2)、left(s1,s2)、right(s1,s2)
				：indexOf(s1,s2,[index],[count])、lastIndexOf(s1,s2,[index],[count])
				：substr(s1,index,[length])、remove(s1,index,[length])
				：replace(s1,s2,s3)
				：regexMatch(input,pattern,'[mir]')
				：regexSplit(input,pattern,'[mir]')
				：regexReplace(input,pattern,replacement,'[mir]')
				：concat(arg1,arg2,arg...,argN)

	日期函数	：date([year],[month],[day],[hour],[minute],[second])
				：year(d)、month(d)、day(d)、hour(d)、minute(d)、second(d)、millisecond(d)
				：dayOfWeek(d)、dateIn(d,beginDate,endDate)、dateAdd(d,x)

	转换函数	：toDate(s)、toInteger(s)、toFloat(s)、toString(o,[format])、toBoolean(s)

	其他函数	：newGuid('N/D/B/P')
				：if(boolean,trueValue,falseValue)
				：in(o,arg1,arg2,arg...,argN)
				：isnull(o,defaultValue)

3、支持参数/函数的(预)定义，字符串拼接（+），
4、支持数字、浮点数、文本（'string')、日期格式（#2012-01-01#）
5、支持以下运算符（一元、二元、三元）：
		
			!	 	!=	 	%	 	&amp;&amp;	 	&amp;	
			(	 	)	 	*	 	+	 	,	
			-	 	/	 	:	 	&lt;	 	&lt;&lt;	
			&lt;=	 	&lt;&gt;	 	=	 	==	 	&gt;	
			&gt;=	 	&gt;&gt;	 	?	 	^	 	and	
			not	 	or	 	|	 	||	 	~	
		
</pre>
                    </li>
                    <li>更多....</li>
                </ul>
            </div>
            <div id="XSqlNodes" class="First">
                X-SQL 元素
            </div>
            <div class="MultiContent">
                本节针对 X-SQL 的各类元素，进行简单的讲解（注意，元素区分大小写，懒判断模式）。
                <div id="XSqlNode_document" class="Third">
                    document 元素
                </div>
                document 元素是一个 .xsql 文件的根级树节点。目前该元素没有任何的属性，因为目前我还没有想好什么全局配置。
                <div id="XSqlNode_xsql" class="Third">
                    xsql 元素
                </div>
                xsql 元素，是 document 元素下的的一个重要元素，表示一个动态的 T-SQL 生成节点。xsql 可以放在两个地方，表示不同的含义。
                <ol>
                    <li>document 元素下，表示一个 X-SQL 的生成函数。</li>
                    <li>parameters 元素下，表示一个立即执行的 X-SQL 元素（这句话比较拗口，简单的说，通过一个 SQL 生成递增序列，添加到参数集合中）。</li>
                </ol>
                <div class="cnblogs_Highlighter">
                    <pre class="brush:html;collapse:true;;gutter:false;">&lt;?xml version="1.0" encoding="utf-8" ?&gt;
    &lt;document&gt;
        &lt;xsql name="selectAllStudent" cache="forever"&gt;
        SELECT
            STUDENT_ID
            ,STUDENT_NAME
            ,STUDENT_SEX
            ,STUDENT_BIRTHDAY
            ,STUDENT_PHOTO
        FROM DEMO_STUDENT
        &lt;/xsql&gt;
    &lt;/document&gt;
                    </pre>
                </div>
                <div id="XSqlNode_expression" class="Third">
                    expression 元素
                </div>
                expression 元素是一个特殊的元素，它表示一段表达式的解释结果（结果可以是任意值，但对于 test 属性，应该是 false）。同样，expression 也可以放在两个不同的地方，代表不同的含义。
                <ol>
                    <li>document 元素下，表示一个可调用的全局表达式（尚未实现，主要是存在性能问题，也可能是设计思路还未成熟）。</li>
                    <li>parameters 元素下，表示一个立即解释的表达式，并将解释后的值添加到参数集合中。</li>
                </ol>
                <div id="XSqlNode_if_else" class="Third">
                    if-else 元素
                </div>
                if 元素是一个判定元素，当表达式结果为 true 时，则编译 if 元素下的 X-SQL 内容，否则编译 else 元素下的内容（如果有）。
                <div class="cnblogs_Highlighter">
                    <pre class="brush:html;collapse:true;;gutter:false;">&lt;?xml version="1.0" encoding="utf-8" ?&gt;
    &lt;document&gt;
      &lt;xsql name="selectAllStudent"
            cache="forever"&gt;
        SELECT
        STUDENT_ID
        ,STUDENT_NAME
        ,STUDENT_SEX
        ,STUDENT_BIRTHDAY
        ,STUDENT_PHOTO
        FROM DEMO_STUDENT
      &lt;/xsql&gt;
      &lt;xsql name="test_if"&gt;
        &lt;include path="selectAllStudent" /&gt;
        &lt;trim prefix="WHERE"
              trim="AND|OR"&gt;
          &lt;if test="studentID==1"&gt;
            AND STUDENT_ID IN (1,2)
          &lt;/if&gt;
          &lt;else&gt;
            AND STUDENT_ID=#{studentID}
          &lt;/else&gt;
          &lt;if test="studentName != null"&gt;
            AND STUDENT_NAME=#{studentName}
          &lt;/if&gt;
        &lt;/trim&gt;
      &lt;/xsql&gt;
    &lt;/document&gt;
                </pre>
            </div>
                <div id="XSqlNode_switch_case_default" class="Third">
                    switch-case-default 元素
                </div>
                同 if-else 元素一样，switch 解释与 case 之间的 test 结果（switch 也拥有 test 属性，但不一定要填写），根据判定条件编译 case 元素下的内容，否则编译 default 元素的内容。
                <div class="cnblogs_Highlighter">
                    <pre class="brush:html;collapse:true;;gutter:false;">&lt;?xml version="1.0" encoding="utf-8" ?&gt;
    &lt;document&gt;
      &lt;xsql name="selectAllStudent"
            cache="forever"&gt;
        SELECT
        STUDENT_ID
        ,STUDENT_NAME
        ,STUDENT_SEX
        ,STUDENT_BIRTHDAY
        ,STUDENT_PHOTO
        FROM DEMO_STUDENT
      &lt;/xsql&gt;
      &lt;xsql name="test_switch" cache="values"&gt;
        &lt;include path="selectAllStudent" /&gt;
        &lt;switch test="studentName"&gt;
          &lt;case test="=='张三'"&gt;
            STUDENT_ID=1
          &lt;/case&gt;
          &lt;case test="=='李四'"&gt;
            STUDENT_ID=2
          &lt;/case&gt;
          &lt;case test="=='王五'"&gt;
            STUDENT_ID=3
          &lt;/case&gt;
          &lt;default&gt;
            STUDENT_ID IS NOT NULL
          &lt;/default&gt;
        &lt;/switch&gt;
      &lt;/xsql&gt;
    &lt;/document&gt;
                    </pre>
                </div>
                <div id="XSqlNode_foreach" class="Third">
                    foreach 元素
                </div>
                foreach 元素是一个非常强大的元素。它解释一个集合类型（array、list等皆可）的参数，并赋予两个“动态绑定参数”：index 和 var。根据 in 参数（集合）进行循环取出结果。注意，in 参数必须存在，如果不存在则会抛出异常（但可以在之前添加一个 if 元素）。 
                <div class="cnblogs_Highlighter">
                    <pre class="brush:html;collapse:true;;gutter:false;">&lt;?xml version="1.0" encoding="utf-8" ?&gt;
    &lt;document&gt;
      &lt;xsql name="selectAllStudent"
            cache="forever"&gt;
        SELECT
        STUDENT_ID
        ,STUDENT_NAME
        ,STUDENT_SEX
        ,STUDENT_BIRTHDAY
        ,STUDENT_PHOTO
        FROM DEMO_STUDENT
      &lt;/xsql&gt;
      &lt;xsql name="test_foreach" cache="names"&gt;
        SELECT * FROM STUDENTS
        &lt;trim prefix="WHERE"
              trim="AND|OR"&gt;
          &lt;if test="studentSex!=null"&gt;
            AND STUDENT_SEX=#{studentSex}
          &lt;/if&gt;
          &lt;if test="studentNames!=null"&gt;
            AND STUDENTNAME IN(
            &lt;foreach var="studentName"
                     in="studentNames"
                     trim=","&gt;
              ,#{studentName}
            &lt;/foreach&gt;
            )
          &lt;/if&gt;
        &lt;/trim&gt;
      &lt;/xsql&gt;
    &lt;/document&gt;
                    </pre>
                </div>
                <div id="XSqlNode_include" class="Third">
                    include 元素
                </div>
                include 元素是文档中使用最频繁的元素之一，它将指定的 xsql 元素编译并添加到当前位置（注，是当前位置！）。关于示例，上面的内容已经有包含了。
                <div id="XSqlNode_trim" class="Third">
                    trim 元素
                </div>
                trim 元素一般用于 where 语句和 set 语句，该元素如果在存在内容时，添加 prefix 属性值，并且自动过滤头尾 trim 属性存在的值。具体示例查看<a href="#XSqlNode_if_else">if-else 元素</a>。
                <div id="XSqlNode_parameters" class="Third">
                    parameters 元素
                </div>
                parameters 元素一般用于自定义参数的情景。例如：ORACLE 插入序列时，需要访问 seq_xxx.nextval 来获取参数序列，并且希望整体 xsql 编译并执行了以后，调用方可以获取到该参数。parameters 可以包含两个子元素：expression 和 xsql 元素。
                <div class="cnblogs_Highlighter">
                    <pre class="brush:html;collapse:true;;gutter:false;">  &lt;xsql name="insert_XML"&gt;
    &lt;parameters&gt;
        &lt;xsql name="student_id"
            cache="forever"&gt;select studentPKSequence.nextVal from dual&lt;/xsql&gt;
        &lt;expression name="student_id3"&gt;student_id+1&lt;/expression&gt;
    &lt;/parameters&gt;
    INSERT INTO STUDENTS(STUDENT_ID,STUDENT_NAME) VALUES(#{student_id,decimal},#{name,string})
    &lt;/xsql&gt;
                    </pre>
                </div>
            </div>
            <div id="XSqlDemos" class="First">
                X-SQL 的实践
            </div>
            <div class="MultiContent">
                <p>我整理了 12 个实例。你可以通过以下 gif 小动画来查看效果。</p>
                <img src="http://images.cnblogs.com/cnblogs_com/sofire/218678/o_1.gif" alt="X-SQL 的实践" />
                <p>虽然我只进行了简单的测试，但 X-SQL 的性能还是十分理想的。在 foreach（该元素最消耗性能） 2000 次测试下，采用缓存是 200~300 毫秒，非缓存效果下 500~600 毫秒（非最佳效果，采用 release，但是在VS 运行测试，而不是直接打开）。</p>
                <p>在大多数情况下，我们建议采用 names（根据参数集合长度、参数名进行缓存）以及 forever（永久缓存）。而不建议使用 values（根据参数值缓存）。</p>
                <p>其实不一定要采用缓存，通过上面的数据可以看出，缓存的后的性能只有一倍之差（缓存的性能损耗：Dict.TryGetValue，可见 X-SQL 的高性能），如果在非硬性要求下，除了 forever 缓存，其他缓存均不推荐。</p>
                
            </div>
            <div id="XSqlEnd" class="First">
                最后
            </div>
            <div class="MultiContent">
                <p>压力测试的必要性还是有的，能否经历的起大项目的高频率、多线程调用，我只能表示“我有信心”。</p>
                <p>目前我将主要精力集中在：如何实现功能。对于性能等其他的要求，我只能延后。</p>
                <p><strong>关于源码</strong></p>
                <p>我很犹豫要不要开放出去，其实从之前的文章大家可以看得出，我秉着开源原则，所以开源是必然，但文章发表后为什么不放上源码，主要有两个考虑：</p>
                <ul>
                    <li>你需要这份源码吗？</li>
                    <li>你会为这份源码提出建议吗？</li>
                </ul>
                <p>如果有兴趣的朋友很多的话，我会开放到 CodePlex。</p>
                <p>最后，对博友 <a href="http://www.cnblogs.com/bk/">blue1000</a>、 <a href="http://www.cnblogs.com/hpze2000/">hpze2000</a> 以及其他朋友说一声抱歉，由于最近公司正在着手新产品的研发，我负责的文档太多了，所以一直耽搁下来了。</p>
                <p>但我很高兴地告诉你们，关于 Sofire.DataComm.Remoting 以及 Sofire.DataComm.Net.Async 模块已经完成，并且新增了许多功能！系列文章一定会写出来的。</p>
                <p><p>Sofire.DataComm下个版本计划(v2.0)<br />1、检查高并发下是否存在死锁（理论上暂时不存在），但在万并发量出现死顿（怀疑是本地网络问题？）。<br />2、增加 buffer 发送前和接收后的（加解密）和（解压缩）。</p>
<p>************************* 2012-07-18 *************************<br />1、修复 ThreadPool.QueueUserWorkItem 在高频发线程发生阻塞的问题。<br />2、增加属性【ServiceVersion】，表示服务契约的版本信息，减少客户端连接时返回契约数据。<br />3、增加属性【SessionExpiration】，表示会话的最大超时时间。小于 0 则表示无超时，等于 0 表示不启用。大于 0 则表示指定的超时秒数。默认为&ldquo;20&rdquo;秒。<br />4、优化内部代码。<br />5、v1.5 版功能已完成。</p></p>
                <p>我写的很用心，如果您对这篇文章感兴趣的话，请推荐……感谢！</p>
            </div>
            <div class="First">
                前文</div>
            <div class="MultiContent">
                <p>
                    紧接上篇内容《<a href="http://www.cnblogs.com/sofire/archive/2012/05/21/2498101.html">Sofire v1.0 开源&mdash;&mdash;WinForm/SL/WebForm 的 Remoting（1）</a>》。</p>
                <p>
                    本文主要讲述 SOFIRE 框架在底层开发中，采用 Sofire.Data 对各种数据库的操作进行统一使用。它仿佛就是对 ADO.NET 的全面封装。</p>
                <p>
                    当然，Sofire.Data 支持哪些数据库，关键是在于实现，而不是在于&ldquo;支持不支持&rdquo;，理论上所有基于 ADO.NET 的数据库全部支持，而其他数据库，也有部分支持。</p>
                <p>
                    当前所支持的数据库包括：MSSQL、Oracle、Oracle、DDTekOracle、OleDb 和 SQLite。至于其他的数据库（如 MySql），由于本人实际项目中并没使用这些数据库，所有暂时不支持，如果你想支持其他数据库，请继承 Sofire.Data.QueryEngineBase。</p>
            </div>
            <div class="First">
                框架约定</div>
            <div class="Third">
                任何函数具有返回内容</div>
            <div>
                在框架的开发过程中，在所有公共函数，都应具有一个返回描述。例如，在处理某业务的函数，可能有人会这样写：
                <div class="cnblogs_Highlighter">
                    <pre class="brush:csharp;gutter:false;">        public DataTable GetTotalReport(string username, DateTime begin, DateTime end)
        {
            //处理过程...
            return new DataTable();
        }</pre>
                </div>
                <p>
                    当然，这并不是指责这样的写法的好坏，而是建议对函数的返回值进行适当的封装描述。例如，我可以这样：</p>
                <div class="cnblogs_Highlighter">
                    <pre class="brush:csharp;gutter:false;">        public Result&lt;DataTable&gt; GetTotalReport(string username, DateTime begin, DateTime end)
        {
            try
            {
                //处理过程...
                return new DataTable();
            }
            catch(Exception ex)
            {
                return ex;
            }
        }</pre>
                </div>
                <p>
                    通过这样的约定，可以明确的告诉函数调用者，这个函数返回一个值，但这个操作函数也可能会返回一个错误的内容。</p>
            </div>
            <div class="Third">
                返回描述，是为了更好的处理</div>
            <div>
                <p>
                    当遇到具有返回内容的操作函数时，可以这样的处理返回结果</p>
                <div class="cnblogs_Highlighter">
                    <pre class="brush:csharp;gutter:false;">        public void Test()
        {
            var r = this.GetTotalReport("a", DateTime.Now, DateTime.Now);
            if(r.IsSucceed)
            {
                DataTable table = r.Value;
            }
            else
            {
                Exception ex = r.Exception;
            }
        }</pre>
                </div>
                <p>
                    简单的代码，表述了返回值具备了函数操作结果的&ldquo;正确性&rdquo;，同时也提供了错误的详细信息。以下是返回结果的接口（IResult）：</p>
                <div class="cnblogs_Highlighter">
                    <div class="cnblogs_Highlighter">
                        <pre class="brush:csharp;collapse:true;;gutter:false;">    /// &lt;summary&gt;
    /// 表示一个结果（接口）。
    /// &lt;/summary&gt;
    public interface IResult
    {
        #region Properties

        /// &lt;summary&gt;
        /// 获取执行时发生的错误。
        /// &lt;/summary&gt;
        Exception Exception
        {
            get;
        }

        /// &lt;summary&gt;
        /// 获取一个值，表示执行结果是否为失败。
        /// &lt;/summary&gt;
        bool IsFailed
        {
            get;
        }

        /// &lt;summary&gt;
        /// 获取一个值，表示执行结果是否为忽略。
        /// &lt;/summary&gt;
        bool IsIgnored
        {
            get;
        }

        /// &lt;summary&gt;
        /// 获取一个值，表示执行结果是否为成功。
        /// &lt;/summary&gt;
        bool IsSucceed
        {
            get;
        }

        /// &lt;summary&gt;
        /// 获取执行的状态。
        /// &lt;/summary&gt;
        ResultState State
        {
            get;
        }

        #endregion Properties

        #region Methods

        /// &lt;summary&gt;
        /// 指定错误信息，将当前结果切换到失败的状态。
        /// &lt;/summary&gt;
        void ToFailded(Exception exception);

        /// &lt;summary&gt;
        /// 指定错误信息，将当前结果切换到失败的状态。
        /// &lt;/summary&gt;
        void ToFailded(string exceptionMessage);

        /// &lt;summary&gt;
        /// 将当前结果切换到忽略的状态。
        /// &lt;/summary&gt;
        void ToIgnored();

        /// &lt;summary&gt;
        /// 将当前结果切换到成功的状态，并且清除结果的错误信息。
        /// &lt;/summary&gt;
        void ToSuccessed();

        /// &lt;summary&gt;
        /// 指示一个结果，与当前结果进行校验。如果 &lt;paramref name="result"/&gt; 是一个错误结果，那么当前的结果将会转换为错误状态。否则将会转换 &lt;paramref name="result"/&gt; 的状态。
        /// &lt;/summary&gt;
        /// &lt;param name="result"&gt;比较的结果。&lt;/param&gt;
        /// &lt;returns&gt;返回一个值，表示结果是否为成功状态。&lt;/returns&gt;
        bool Checking(Result result);

        #endregion Methods
    }


    /// &lt;summary&gt;
    /// 表示包含一个返回值的结果（接口）。
    /// &lt;/summary&gt;
    /// &lt;typeparam name="TValue"&gt;返回值的类型。&lt;/typeparam&gt;
    public interface IResult&lt;TValue&gt; : IResult
    {
        #region Properties

        /// &lt;summary&gt;
        /// 设置或获取结果的返回值。若结果不处于成功的状态，将返回默认值。
        /// &lt;/summary&gt;
        TValue Value
        {
            get; set;
        }

        #endregion Properties

        #region Methods

        /// &lt;summary&gt;
        /// 将当前结果切换到成功的状态，并且清除结果的错误信息。
        /// &lt;/summary&gt;
        /// &lt;param name="value"&gt;结果返回的值。&lt;/param&gt;
        void ToSuccessed(TValue value);

        /// &lt;summary&gt;
        /// 指示一个结果，与当前结果进行校验。如果 &lt;paramref name="result"/&gt; 是一个错误结果，那么当前的结果将会转换为错误状态。否则将会转换 &lt;paramref name="result"/&gt; 的状态。
        /// &lt;/summary&gt;
        /// &lt;param name="result"&gt;比较的结果。&lt;/param&gt;
        /// &lt;param name="value"&gt;状态为成功时的返回值。&lt;/param&gt;
        /// &lt;returns&gt;返回一个值，表示结果是否为成功状态。&lt;/returns&gt;
        bool Checking(Result result, TValue value);

        #endregion Methods
    }

    /// &lt;summary&gt;
    /// 表示包含两个返回值的结果（接口）。
    /// &lt;/summary&gt;
    /// &lt;typeparam name="TValue1"&gt;第一个返回值的类型。&lt;/typeparam&gt;
    /// &lt;typeparam name="TValue2"&gt;第二个返回值的类型。&lt;/typeparam&gt;
    public interface IResult&lt;TValue1, TValue2&gt; : IResult
    {
        #region Properties

        /// &lt;summary&gt;
        /// 设置或获取结果的第一个返回值。若结果不处于成功的状态，将返回默认值。
        /// &lt;/summary&gt;
        TValue1 Value1
        {
            get; set;
        }

        /// &lt;summary&gt;
        /// 设置或获取结果的第二个返回值。若结果不处于成功的状态，将返回默认值。
        /// &lt;/summary&gt;
        TValue2 Value2
        {
            get; set;
        }

        #endregion Properties

        #region Methods

        /// &lt;summary&gt;
        /// 将当前结果切换到成功的状态，并且清除结果的错误信息。
        /// &lt;/summary&gt;
        /// &lt;param name="value1"&gt;结果的第一个返回值。&lt;/param&gt;
        /// &lt;param name="value2"&gt;结果的第二个返回值。&lt;/param&gt;
        void ToSuccessed(TValue1 value1, TValue2 value2);

        /// &lt;summary&gt;
        /// 指示一个结果，与当前结果进行校验。如果 &lt;paramref name="result"/&gt; 是一个错误结果，那么当前的结果将会转换为错误状态。否则将会转换 &lt;paramref name="result"/&gt; 的状态。
        /// &lt;/summary&gt;
        /// &lt;param name="result"&gt;比较的结果。&lt;/param&gt;
        /// &lt;param name="value1"&gt;状态为成功时的第一个返回值。&lt;/param&gt;
        /// &lt;param name="value2"&gt;状态为成功时的第二个返回值。&lt;/param&gt;
        /// &lt;returns&gt;返回一个值，表示结果是否为成功状态。&lt;/returns&gt;
        bool Checking(Result result, TValue1 value1, TValue2 value2);

        #endregion Methods
    }
</pre>
                    </div>
                </div>
                <p>
                    &nbsp;</p>
            </div>
            <div class="First">
                Sofire.Data</div>
            <div class="Third">
                数据库是如何连接的？</div>
            <div>
                <p>
                    Sofire.Data 的所有数据库实现，都派生于 Sofire.Data.QueryEngineBase，通过简单的几个抽象实现，从而达到对数据库的快速支持。</p>
                <div class="cnblogs_Highlighter">
                    <pre class="brush:csharp;gutter:false;">        public void DataConnect()
        {
            string connectionString = "";
            OracleQuery oracleQuery = new OracleQuery(connectionString); // 微软已经不建议使用这种方式连接 Oracle
            DDTekOracleQuery oleDbQuery = new DDTekOracleQuery(connectionString);
            MsSqlQuery mssqlQuery = new MsSqlQuery(connectionString);
            OleDbQuery oleDbQuery = new OleDbQuery(connectionString);
            SQLiteQuery sqliteQuery = new SQLiteQuery(connectionString);
            // 派生基类 QueryEngineBase 扩展，可以对更多的数据库提供支持。
        }</pre>
                </div>
            </div>
            <div class="Third">
                数据库是如何查询的？</div>
            <div>
                <p>
                    数据库的查询</p>
                <div class="cnblogs_Highlighter">
                    <pre class="brush:csharp;gutter:false;">        public void Execute()
        {
            string connectionString = "";
            int uid = 1;
            DDTekOracleQuery oracleQuery = new DDTekOracleQuery(connectionString);
            TableResult r1 = oracleQuery.ExecuteTable("SELECT * FROM Users WHERE UID=:uid", "uid", uid);
            
            if(r1.IsSucceed)
            {
                DataTable table = r1.Value;
            }

            SqlQuery mssqlQuery = new SqlQuery(connectionString);
            TableResult r2 = mssqlQuery.ExecuteTable("SELECT * FROM Users WHERE UID=@uid", "@uid", uid);
            
            if(r2.IsSucceed)
            {
                DataTable table = r2.Value;
            }
        }</pre>
                </div>
                <p>
                    当然，上面的演示代码仅仅是返回一张表，更多的支持请参考以下图片</p>
                <p>
                    <img src="http://images.cnblogs.com/cnblogs_com/sofire/218678/r_QQ%e6%88%aa%e5%9b%be20120522214454.png" alt="" width="278" height="464" /></p>
                <p>
                    由于今年的工作关系，我对 Oracle 的接触频繁，Sofire.Data 中对于 Oracle 的支持，也逐渐成熟中，例如支持多行 ExecuteNoQuery，支持游标参数。</p>
                <div class="cnblogs_Highlighter">
                    <pre class="brush:csharp;gutter:false;">        private Result Test1()
        {
            //&ldquo;&gt;&rdquo;符号表示这是一个存储过程，或程序包
            // PKG_FLOW_NAME.UP_GetFlowNameById 的返回值在于一个游标参数。
            var tableResult = query.ExecuteTable("&gt;PKG_FLOW_NAME.UP_GetFlowNameById"
                , new ExecuteParameter("v_FID", 111, DbType.VarNumeric)
                , query.CreateCursor("c"));

            if(tableResult.IsSucceed)
            {
                Console.WriteLine(tableResult.Value.Rows.Count);
            }
            return tableResult;
        }
        private Result Test2()
        {
            var noQueryResult = query.ExecuteNoQuery(@"begin
            insert into table1 select * from XXX;
            insert into table2 select * from XXX;
            insert into table3 select * from XXX;
            insert into table4 select * from XXX;
            end;");

            if(tableResult.IsSucceed)
            {
                Console.WriteLine(tableResult.Value.Rows.Count);
            }
            return tableResult;
        }</pre>
                </div>
            </div>
            <div class="Third">
                结束语</div>
            <div>
                <p>
                    由于时间的关系（最近工作岗位变动），最重要，也是这套框架元老级组件&mdash;&mdash;数据库部分，介绍的并不详细。接下来，可能对 Sofire.Data 的高级部分进行讲解，比如对查询前后的事件支持，查询结果自动转换为对象/集合。不过这种比较代码性的东西，的确比较难阅读，也让我异常纠结。</p>
                <p>
                    额，好像很多人想觉得我这款博客皮肤不错？其实这一款博客皮肤是参考 <span style="color: #ff0000;"><a href="http://www.cnblogs.com/libaoheng/"><span style="color: #ff0000;">李宝亨达人</span></a></span> 的博客风格进行改版的，很感谢他的皮肤（他是绿色版，我是蓝色版）。</p>
                <p>
                    <strong><span style="color: #ff0000;">我很懒，但如果您在使用这套组件中遇见任何问题或者有任何建议意见，可以在博客留言，我将会及时回复。源码已更新。稍后上传。</span></strong></p>
            </div>
        </div>
    </div>
</body>
</html>
